bitkeeper revision 1.1041.5.5 (40e97f82YUOF-rGuIapKOE73WUHZcg)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 5 Jul 2004 16:19:14 +0000 (16:19 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 5 Jul 2004 16:19:14 +0000 (16:19 +0000)
Add control over domain restart on shutdown.

tools/examples/xmdefaults
tools/examples/xmnetbsd
tools/python/xen/xend/XendClient.py
tools/python/xen/xend/XendDomain.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/server/SrvDomain.py
tools/python/xen/xm/create.py
tools/python/xen/xm/shutdown.py

index cf9e9861db9368661b6055e72e3e5b3ed8a32bf9..640a7390a03338fd4aaa507c177bf7bb203614b7 100644 (file)
@@ -2,7 +2,6 @@
 #============================================================================
 # Python defaults setup for 'xm create'.
 # Edit this file to reflect the configuration of your system.
-
 #============================================================================
 
 # Define script variables here.
@@ -94,6 +93,6 @@ extra = "4 VMID=%d usr=/dev/sda6" % vmid
 #----------------------------------------------------------------------------
 # Set according to whether you want the domain  restarted when it exits.
 # The default is False.
-#restart = True
+#autorestart = True
 
 #============================================================================
index 0563239464d3064429241907d67dde86088fea30..4f920087b65ae2c160cf18453e066488613559d3 100644 (file)
@@ -2,24 +2,23 @@
 #============================================================================
 # Python defaults setup for 'xm create'.
 # Edit this file to reflect the configuration of your system.
-# This file expects the variable 'vmid' to be set.
 #============================================================================
 
-def config_usage ():
-    print >>sys.stderr,"""
-The config file '%s' requires the following variable to be defined:
- vmid             -- Numeric identifier for the new domain, used to calculate
-                     the VM's IP address and root partition. E.g. -Dvmid=1
-""" % config_file
+# Define script variables here.
+# xm_vars is defined automatically, use xm_vars.var() to define a variable.
 
+def vmid_check(var, val):
+    val = int(val)
+    if val <= 0:
+        raise ValueError
+    return val
+    
+xm_vars.var('vmid',
+            use="Virtual machine id. Integer greater than 0.",
+            check=vmid_check)
 
-try:
-    vmid = int(vmid) # convert to integer
-except:
-    raise ValueError, "Variable 'vmid' must be an integer"
-
-if vmid <= 0:
-    raise ValueError, "Variable 'vmid' must be greater than 0" 
+# This checks the script variables.
+xm_vars.check()
 
 #----------------------------------------------------------------------------
 # Kernel image file.
@@ -97,6 +96,6 @@ extra = "4 VMID=%d bootdev=xennet0" % vmid
 #----------------------------------------------------------------------------
 # Set according to whether you want the domain  restarted when it exits.
 # The default is False.
-#restart = True
+#autorestart = True
 
 #============================================================================
index 12a21c2c9c8507097a1a074255fb5ce35cad9551..b5d57b7174455256b0474fb200074b2e590a489b 100644 (file)
@@ -225,9 +225,10 @@ class Xend:
         return xend_call(self.domainurl(id),
                          {'op'      : 'pause'})
 
-    def xend_domain_shutdown(self, id):
+    def xend_domain_shutdown(self, id, reason):
         return xend_call(self.domainurl(id),
-                         {'op'      : 'shutdown'})
+                         {'op'      : 'shutdown',
+                          'reason'  : reason })
 
     def xend_domain_destroy(self, id):
         return xend_call(self.domainurl(id),
index 328450a6b5753693389b0165f1750ceb53c8deaf..4bde519b498a776eeca5b76bcbb3ab86426aba17 100644 (file)
@@ -5,8 +5,10 @@
  Needs to be persistent for one uptime.
 """
 import sys
+import traceback
 
 from twisted.internet import defer
+from twisted.internet import reactor
 
 import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
 
@@ -28,10 +30,19 @@ __all__ = [ "XendDomain" ]
 class XendDomain:
     """Index of all domains. Singleton.
     """
-    
+
+    """Path to domain database."""
     dbpath = "domain"
+
+    """Table of domain info indexed by domain id."""
     domain = {}
     
+    """Table of configs for domain restart, indexed by domain id."""
+    restarts = {}
+
+    """Table of delayed calls."""
+    schedule = {}
+    
     def __init__(self):
         self.xconsole = XendConsole.instance()
         # Table of domain info indexed by domain id.
@@ -49,6 +60,53 @@ class XendDomain:
         print 'XendDomain> virq', val
         self.reap()
 
+    def schedule_later(self, _delay, _name, _fn, *args):
+        """Schedule a function to be called later (if not already scheduled).
+
+        _delay delay in seconds
+        _name  schedule name
+        _fn    function
+        args   arguments
+        """
+        if self.schedule.get(_name): return
+        self.schedule[_name] = reactor.callLater(_delay, _fn, *args)
+        
+    def schedule_cancel(self, name):
+        """Cancel a scheduled function call.
+        
+        name schedule name to cancel
+        """
+        callid = self.schedule.get(name)
+        if not callid:
+            return
+        if callid.active():
+            callid.cancel()
+        del self.schedule[name]
+
+    def reap_schedule(self, delay=0):
+        """Schedule reap to be called later.
+
+        delay delay in seconds
+        """
+        self.schedule_later(delay, 'reap', self.reap)
+
+    def reap_cancel(self):
+        """Cancel any scheduled reap.
+        """
+        self.schedule_cancel('reap')
+
+    def refresh_schedule(self, delay=0):
+        """Schedule refresh to be called later.
+        
+        delay delay in seconds
+        """
+        self.schedule_later(delay, 'refresh', self.refresh)
+
+    def refresh_cancel(self):
+        """Cancel any scheduled refresh.
+        """
+        self.schedule_cancel('refresh')
+
     def rm_all(self):
         """Remove all domain info. Used after reboot.
         """
@@ -145,6 +203,7 @@ class XendDomain:
         """Look for domains that have crashed or stopped.
         Tidy them up.
         """
+        self.reap_cancel()
         print 'XendDomain>reap>'
         domlist = xc.domain_getinfo()
         casualties = []
@@ -158,12 +217,14 @@ class XendDomain:
         for d in casualties:
             id = str(d['dom'])
             print 'XendDomain>reap> died id=', id, d
-            self.domain_destroy(id, refresh=0)
+            self.final_domain_destroy(id)
         print 'XendDomain>reap<'
 
     def refresh(self):
         """Refresh domain list from Xen.
         """
+        self.refresh_cancel()
+        print 'XendDomain>refresh>'
         domlist = xc.domain_getinfo()
         # Index the domlist by id.
         # Add entries for any domains we don't know about.
@@ -184,7 +245,7 @@ class XendDomain:
                 d.update(dominfo)
             else:
                 self._delete_domain(d.id)
-        self.reap()
+        self.reap_schedule(1)
 
     def refresh_domain(self, id):
         """Refresh information for a single domain.
@@ -267,21 +328,70 @@ class XendDomain:
         """Shutdown domain (nicely).
 
         id     domain id
-        reason shutdown type: poweroff, reboot, halt
+        reason shutdown type: poweroff, reboot, suspend, halt
         """
         dom = int(id)
         if dom <= 0:
             return 0
+        self.domain_restart_schedule(id, reason)
         eserver.inject('xend.domain.shutdown', [id, reason])
+        if reason == 'halt':
+            reason = 'poweroff'
         val = xend.domain_shutdown(dom, reason)
-        self.refresh()
+        self.refresh_schedule()
         return val
-    
-    def domain_destroy(self, id, refresh=1):
-        """Terminate domain immediately.
+
+    def domain_restart_schedule(self, id, reason):
+        """Schedule a restart for a domain if it needs one.
+
+        id     domain id
+        reason shutdown reason
+        """
+        if id in self.restarts:
+            # Don't schedule if already there.
+            return
+        restart = 0
+        if reason in ['poweroff', 'reboot']:
+            dominfo = self.domain.get(id)
+            if dominfo and (dominfo.autorestart or reason == 'reboot'):
+                restart = 1
+                # Clear autorestart flag to avoid multiple restarts.
+                dominfo.autorestart = 0
+            
+        if restart:
+            self.restarts[id] = dominfo.config
+            
+    def domain_restart_cancel(self, id):
+        """Cancel any restart scheduled for a domain.
+
+        id domain id
+        """
+        dominfo = self.domain.get(id)
+        if dominfo:
+            dominfo.autorestart = 0
+        if id in self.restarts:
+            del self.restarts[id]
+
+    def domain_restarts(self):
+        """Execute any scheduled domain restarts for domains that have gone.
+        """
+        for id in self.restarts.keys():
+            if id in self.domain:
+                # Don't execute restart for domains still running.
+                continue
+            config = self.restarts[id]
+            # Remove it from the restarts.
+            del self.restarts[id]
+            try:
+                self.domain_create(config)
+            except:
+                print >>sys.stderr, "XendDomain> Exception restarting domain"
+                traceback.print_exc(sys.stderr)
+        
+    def final_domain_destroy(self, id):
+        """Final destruction of a domain..
 
         id domain id
-        refresh send a domain destroy event if true
         """
         dom = int(id)
         if dom <= 0:
@@ -292,15 +402,26 @@ class XendDomain:
             val = dominfo.destroy()
         else:
             val = xc.domain_destroy(dom=dom)
-        if refresh: self.refresh()
         return val       
 
+    def domain_destroy(self, id):
+        """Terminate domain immediately.
+        Camcels any restart for the domain.
+
+        id domain id
+        """
+        self.domain_restart_cancel(id)
+        val = self.final_domain_destroy(id)
+        self.refresh_schedule()
+        return val
+
     def domain_migrate(self, id, dst):
         """Start domain migration.
 
         id domain id
         """
         # Need a cancel too?
+        # Don't forget to cancel restart for it.
         pass
 
     def domain_save(self, id, dst, progress=0):
index 869fc28e07c7c6002ea26418f4f7330b598248eb..f14e6477c28c388e3e8d22e3ca787666ef55f1d2 100644 (file)
@@ -362,6 +362,8 @@ class XendDomainInfo:
         self.state = self.STATE_OK
         #todo: set to migrate info if migrating
         self.migrate = None
+        #Whether to auto-restart
+        self.autorestart = 0
 
     def setdom(self, dom):
         self.dom = int(dom)
@@ -418,6 +420,8 @@ class XendDomainInfo:
         try:
             self.name = sxp.child_value(config, 'name')
             self.memory = int(sxp.child_value(config, 'memory', '128'))
+            if sxp.child(config, 'autorestart', None):
+                self.autorestart = 1
             self.configure_backends()
             image = sxp.child_value(config, 'image')
             image_name = sxp.name(image)
index 156198bd70d0700417d7c93bf6f2d4ab621cff52..085dd2642eb417261ceb915f9dc6ad1f5abbfa30 100644 (file)
@@ -27,7 +27,11 @@ class SrvDomain(SrvDir):
         return val
 
     def op_shutdown(self, op, req):
-        val = self.xd.domain_shutdown(self.dom.id)
+        #val = self.xd.domain_shutdown(self.dom.id)
+        fn = FormFn(self.xd.domain_shutdown,
+                    [['dom', 'int'],
+                     ['reason', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
         req.setResponseCode(202)
         req.setHeader("Location", "%s/.." % req.prePathURL())
         return val
index 36bcddef92e5271e0ae62bd26d6bb8f22506faff..3c61db9325e260b2037dd6241eb5971e1f4d77e9 100644 (file)
@@ -101,6 +101,10 @@ gopts.opt('memory', short='m', val='MEMORY',
          fn=set_value, default=128,
          use="Domain memory in MB.")
 
+gopts.opt('autorestart',
+         fn=set_true, default=0,
+         use="Whether to restart the domain on exit.")
+
 gopts.opt('blkif',
           fn=set_true, default=0,
           use="Make the domain a block device backend.")
@@ -271,6 +275,8 @@ def make_config(opts):
         config.append(['backend', ['blkif']])
     if opts.netif:
         config.append(['backend', ['netif']])
+    if opts.autorestart:
+        config.append(['autorestart'])
     
     configure_image(config, opts)
     config_devs = []
index aaa354554aba283c9e6274b2214d9cc81c3b4528..926aaf27f3bee7544fca09e9181610462fdb3e5f 100644 (file)
@@ -1,4 +1,5 @@
 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
 """Domain shutdown.
 """
 import string
@@ -10,7 +11,8 @@ from xen.xm.opts import *
 
 gopts = Opts(use="""[options] [DOM]
 
-Shutdown one or more domains gracefully.""")
+Shutdown one or more domains gracefully.
+""")
 
 gopts.opt('help', short='h',
          fn=set_true, default=0,
@@ -24,18 +26,22 @@ gopts.opt('wait', short='w',
          fn=set_true, default=0,
          use='Wait for shutdown to complete.')
 
-gopts.opt('norestart', short='n',
+gopts.opt('halt', short='H',
+          fn=set_true, default=0,
+          use='Shutdown without reboot.')
+
+gopts.opt('reboot', short='R',
           fn=set_true, default=0,
-          use='Prevent domain restart.')
+          use='Shutdown and reboot.')
 
-def shutdown(opts, doms, wait):
+def shutdown(opts, doms, mode, wait):
     def domains():
         return [ int(a) for a in server.xend_domains() ]
     if doms == None: doms = domains()
     if 0 in doms:
         doms.remove(0)
     for d in doms:
-        server.xend_domain_shutdown(d)
+        server.xend_domain_shutdown(d, mode)
     if wait:
         while doms:
             alive = domains()
@@ -49,6 +55,21 @@ def shutdown(opts, doms, wait):
             time.sleep(1)
         opts.info("All domains terminated")
 
+def shutdown_mode(opts):
+    mode = 'poweroff'
+    if opts.vals.wait:
+        mode = 'halt'
+        if opts.vals.reboot:
+           opts.err("Can't specify wait and reboot") 
+    else:
+        if opts.vals.halt and opts.vals.reboot:
+            opts.err("Can't specify halt and reboot")
+        if opts.vals.halt:
+            mode = 'halt'
+        elif opts.vals.reboot:
+            mode = 'reboot'
+    return mode
+
 def main_all(opts, args):
     shutdown(opts, None, opts.vals.wait)
 
@@ -59,7 +80,9 @@ def main_dom(opts, args):
         domid = int(dom)
     except:
         opts.err('Invalid domain: ' + dom)
-    shutdown(opts, [ domid ], opts.vals.wait)
+        
+    mode = shutdown_mode(opts)  
+    shutdown(opts, [ domid ], mode, opts.vals.wait)
     
 def main(argv):
     opts = gopts